JavaScript 手写实现#
#原生行为/方法
#instanceof
function myInstanceof(child, parent) {
if (typeof parent !== 'function') throw `${parent} is not a function`
const proto = Object.getPrototypeOf(child)
while (proto) {
if (proto === parent.prototype) {
return true
}
proto = Object.getPrototypeOf(proto)
}
return false
}#改变 instanceof 的行为(了解
class Person {
static [Symbol.hasInstance](op) {
return op.__proto__ === this.prototype
}
constructor(name) {
this.name = name
}
}
const person = new Person('xz')
console.log(person instanceof Person) // true#Object.create
function myObjectCreate(proto) {
const obj = {}
Object.setPrototypeOf(obj, proto)
return obj
}#new
function myNew(Fun, ...args) {
if (typeof Fun !== 'function') throw `${Fun} is not a function`
const obj = Object.create(Fun.prototype)
const res = Fun.apply(obj, args)
if ((typeof res === 'object' || typeof res === 'function') && res !== null) {
return res
}
return obj
}#bind call apply
Function.prototype.myCall = function (bindThis, ...args) {
bindThis = bindThis === undefined || bindThis === null ? globalThis : Object(bindThis)
const fun = this
const key = Symbol()
bindThis[key] = fun
const res = bindThis[key](...args)
delete bindThis[key]
return res
}
Function.prototype.myApply = function (bindThis, args = []) {
bindThis = bindThis === undefined || bindThis === null ? globalThis : Object(bindThis)
const fun = this
const key = Symbol()
bindThis[key] = fun
const res = bindThis[key](...args)
delete bindThis[key]
return res
}
Function.prototype.myBind = function (bindThis, ...args) {
const fun = this
const foo = function (...innerArgs) {
// 处理 new 方式调用
if (this instanceof fun) {
return fun.apply(this, args.concat(innerArgs))
}
return fun.apply(bindThis, args.concat(innerArgs))
}
Object.setPrototypeOf(foo, fun.prototype)
return foo
}#手动改写隐式转化流程(了解
原理:对象身上有一个 Symbol.toPrimitive 方法,这个方法接受一个 hint 转换参数,默认情况 hint 为
string:先执行 valueOf 在执行 toString (String、模板字符串 ``
number:先执行 toString,再执行 valueOf (Number、正负号+-
default:先执行 valueOf 在执行 toString(隐式转化
const obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case "string":
return "obj-toPrimitive-string";
case "number":
return 1;
case "default":
return "obj-toPrimitive-default";
}
},
};
console.log(`${obj}`); // obj-toPrimitive-string
console.log(+obj); // 1
console.log(obj + ""); // obj-toPrimitive-default#Array.forEach
Array.prototype.forEach = function (fun, thisArg) {
if (typeof fun !== "function") throw `${fun} is not a function`;
const arr = this;
for (let i = 0; i < arr.length; i++) {
// 处理空元素
if (i in arr) {
fun.call(thisArg, arr[i], i, arr);
}
}
};#Array.map
Array.prototype.map = function (fun, thisArg) {
if (typeof fun !== "function") throw `${fun} is not a function`;
const arr = this;
const resArr = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
if (i in arr) {
resArr[i] = fun.call(thisArg, arr[i], i, arr);
}
}
return resArr;
};#Array.filter
Array.prototype.filter = function (fun, thisArg) {
if (typeof fun !== "function") throw `${fun} is not a function`;
const arr = this;
const resArr = [];
for (let i = 0; i < arr.length; i++) {
if (i in arr) {
const res = fun.call(thisArg, arr[i], i, arr);
res && resArr.push(res);
}
}
return resArr;
};#Array.reduce
Array.prototype.reduce = function (fun, init) {
if (typeof fun !== "function") throw `${fun} is not a function`;
const arr = this;
let acc = init;
let cur = 0;
// 找到第一个非空元素
if (init === undefined) {
while (cur < arr.length && !(cur in arr)) cur++;
if (cur === arr.length) throw "Each element of the array is empty";
acc = arr[cur++];
}
for (; cur < arr.length; cur++) {
if (cur in arr) {
// reduce 无this绑定
acc = fun.call(undefined, acc, arr[cur], cur, arr);
}
}
return acc;
};#工具函数
#柯里化
function curry(fun, ...args) {
return function (...innerArgs) {
const allArgs = args.concat(innerArgs)
if (allArgs.length < fun.length) {
return curry.call(this, fun, ...allArgs)
} else {
return fun.apply(this, allArgs)
}
}
}#深浅拷贝
function shallowClone(target) {
if (typeof target !== 'object' || target === null) return target
const obj = target.constructor()
for (const key in target) {
if (Object.hasOwnProperty.call(target, key)) {
obj[key] = target[key]
}
}
return obj
}
function deepClone(target, cache = new WeakMap()) {
if ((typeof target !== 'object' && typeof target !== 'function') || target === null) return target
if (target instanceof Date) return new Date(target)
if (target instanceof RegExp) return new RegExp(target.source, target.flags)
if (target instanceof WeakMap) return new WeakMap()
if (target instanceof WeakSet) return new WeakSet()
// 这里可以再forin克隆一下函数身上的属性
if (target instanceof Function) {
if (target.prototype) {
return function (...args) {
return target.apply(this, args)
}
} else {
return (...args) => {
return target(...args)
}
}
}
if (cache.has(target)) return cache.get(target)
const obj = target.constructor()
cache.set(target, obj)
if (target instanceof Map) {
target.forEach((value, key) => {
obj.set(deepClone(key, cache), deepClone(value, cache))
});
}
if (target instanceof Set) {
target.forEach((value) => {
obj.add(deepClone(value, cache))
});
}
for (const key in target) {
if (Object.hasOwnProperty.call(target, key)) {
obj[key] = deepClone(target[key], cache)
}
}
return obj
}#防抖节流
const throttle = (fn, delay) => {
let pre = Date.now()
return function (...args) {
const current = Date.now()
if (current - pre < delay) {
return
}
pre = current
return fn.apply(this, args)
}
}
const debounce = (fn, delay) => {
let timer = null
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
function debounce(fun, delay, immediate) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
// timer 控制当前是否 delay 外的第一次执行
if (immediate && !timer) {
fun.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, delay);
return;
}
timer = setTimeout(() => {
timer = null;
fun.apply(this, args);
}, delay);
};
}#loadsh.get
#版本号对比
#url解析
#场景/应用

#图片懒加载
function init() {
const container = document.documentElement || document.body;
const imgs = container.querySelectorAll("img");
const GAP = 100;
let count = 0;
function lazyLoad() {
const clientHeight = container.clientHeight;
const scrollTop = container.scrollTop;
for (let i = count; i < imgs.length; i++) {
if (imgs[i].offsetTop <= clientHeight + scrollTop + GAP) {
imgs[i].src = imgs[i].dataset.src;
count++;
}
}
}
container.addEventListener("scroll", throttle(lazyLoad, 100));
}function init() {
const container = document.documentElement || document.body;
const imgs = container.querySelectorAll("img");
const GAP = 100;
let count = 0;
function lazyLoad() {
const clientHeight = container.clientHeight;
const scrollTop = container.scrollTop;
for (let i = count; i < imgs.length; i++) {
// 与上不同
if (imgs[i].getBoundingClientRect().top <= clientHeight + GAP) {
imgs[i].src = imgs[i].dataset.src;
count++;
}
}
}
container.addEventListener("scroll", throttle(lazyLoad, 100));
}function init() {
const container = document.documentElement || document.body;
const imgs = container.querySelectorAll("img");
const GAP = 100;
const observe = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observe.unobserve(img);
}
});
},
{
// 指定容器
root: container,
// 预加载;与 margin 规则一致
rootMargin: `${GAP}px`,
}
);
imgs.forEach((img) => observe.observe(img));
}#LRU
#设计模式
#观察者
class Subject {
constructor() {
this.watchers = new Set();
}
add(watcher) {
this.watchers.add(watcher);
}
remove(watcher) {
this.watchers.delete(watcher);
}
notify(...data) {
this.watchers.forEach((watcher) => {
watcher.update(...data);
});
}
}
class Watcher {
constructor(update) {
this.update = update;
}
}
const watcher1 = new Watcher();
const watcher2 = new Watcher();
const watcher3 = new Watcher();
const sub = new Subject();
sub.add(watcher1);
sub.add(watcher2);
sub.add(watcher3);
sub.notify("");#发布订阅
class EventBus {
constructor() {
this.events = new Map();
}
on(eventName, callback) {
if (typeof callback !== "function") {
throw new TypeError(`${callback} is not a function`);
}
if (this.events.has(eventName)) {
this.events.get(eventName).push(callback);
} else {
this.events.set(eventName, [callback]);
}
return () => {
this.off(eventName, callback);
};
}
emit(eventName, ...data) {
const callbacks = this.events.get(eventName);
if (!callbacks?.length) {
return;
}
// 避免回调中调用 on/off 修改到原数组,进行浅拷贝
for (const cb of [...callbacks]) {
try {
cb(...data);
} catch (error) {
console.error(`事件 ${eventName} 的某个监听执行失败`, error);
}
}
}
off(eventName, callback) {
// 全删除
if (!callback) {
this.events.delete(eventName);
return;
}
const callbacks = this.events.get(eventName);
if (!callbacks) {
return;
}
const index = callbacks.indexOf(callback);
if (index !== -1) {
callbacks.splice(index, 1);
}
if (callbacks.length === 0) {
this.events.delete(eventName);
}
}
once(eventName, callback) {
if (typeof callback !== "function") {
throw new TypeError("EventBus.once: callback 必须是函数");
}
const wrapper = (...data) => {
try {
callback(...data);
} finally {
this.off(eventName, wrapper);
}
};
this.on(eventName, wrapper);
}
clear() {
this.events.clear();
}
}#Promise专项
#Promise
#Promise.reslove
Promise.resolve = function (param) {
// 1. 传参是 promise 直接返回
if (param instanceof Promise) return param
return new Promise((resolve, reject) => {
// 2. 如果是 thenable 对象,跟随它的结果
if (param && param.then && typeof param.then === 'function') {
return param.then(resolve, reject)
}
// 3. 其他直接成功
else {
resolve(param)
}
})
}#Promise.reject
Promise.reject = function (reason) {
return new Promise((_, reject) => {
reject(reason)
})
}#Promise.all
Promise.all = function (queue) {
return new Promise((resolve, reject) => {
// 处理可迭代对象
queue = Array.from(queue);
if (queue.length === 0) return resolve([]);
const res = new Array(queue.length);
let count = 0;
for (let i = 0; i < queue.length; i++) {
// 考虑到 queue[i] 不是 promise
Promise.resolve(queue[i]).then(
(result) => {
res[i] = result;
count++;
// 所有promise都成功
if (count === queue.length) {
resolve(res);
}
},
(reason) => {
reject(reason);
}
);
}
});
};#Promise.race
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
// 处理可迭代对象
promises = Array.from(promises)
// 空数组永远 pending
if (promises.length === 0) return
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(
result => {
resolve(result)
},
reason => {
reject(reason)
})
}
})
}#Promise.allSettled
Promise.allSettled = function (promises) {
return new Promise((resolve) => {
promises = Array.from(promises)
if (promises.length === 0) return resolve([])
const res = new Array(promises.length)
let count = 0
promises.forEach((promise, index) => {
// 处理非 promise
Promise.resolve(promise).then(value => {
res[index] = { status: 'fulfilled', value }
}, reason => {
res[index] = { status: 'rejected', reason }
}).finally(() => {
count++
if (promises.length === count) resolve(res)
})
})
})
}